# z80jit - Z80 CPU emulator [with JIT compilation?], in rpython.

# Copyright (C) 2017 deesix <deesix@tuta.io>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

from os import write
import z80
try:
    from rpython.rlib.jit import JitDriver
except ImportError:
    # Mock for CPython compatibility.
    class JitDriver(object):
        def __init__(self,**kw): pass
        def jit_merge_point(self,**kw): pass
        def can_enter_jit(self,**kw): pass

class DummyIO():
    def wr(self, a, b): pass
    def rd(self, a): return 0x42

def file_content_as_list_of_bytes(fname):
    bx = []
    for s in open(fname, 'rb').read():
        bx.append(ord(s) & 0xff)
    return bx

def ram_64k_with_program(filename, addr=0):
    program = file_content_as_list_of_bytes(filename)
    mem = [0]*addr + program
    mem = mem + [0]*(65536 - len(mem))
    assert len(mem) == 0x10000
    return mem

def patch_loader_and_handler(mem):
    mem[0]=0xC3
    mem[1]=0x00
    mem[2]=0x01
    mem[5]=0xC9

def cpm_call9(cpu):
    mem = cpu.mem
    p = cpu._get_de()
    msg = []
    while mem[p] != ord('$'):
        msg.append(chr(mem[p]))
        p += 1
    write(1, "".join(msg))
    return 0

def cpm_call2(cpu):
    write(1, chr(cpu.e))
    return 0

jitdriver = JitDriver(greens=['pc'], reds=['mem', 'cpu'])
def zextests(filename):
    mem = ram_64k_with_program(filename, 0x0100)
    cpu = z80.cpu(mem, DummyIO())
    patch_loader_and_handler(mem)
    pc = 0
    while True:
        pc = cpu._get_pc()
        if pc == 0x5:
            if cpu.c == 2: cpm_call2(cpu)
            if cpu.c == 9: cpm_call9(cpu)
            if cpu.c == 0: break
        jitdriver.jit_merge_point(pc=pc, mem=mem, cpu=cpu)#, filename=filename)
        try:
            cpu.execute()
        except z80.Error, e:
            cpu._set_pc(pc)
            print('\n%s at %s\n' % (e, pc))
            break
        if cpu._get_pc() == 0:
            print('')
            break
        if cpu._get_pc() < pc:
            jitdriver.can_enter_jit(pc=cpu._get_pc(), mem=mem, cpu=cpu)

def entry_point(args):
    zextests('zexdoc.com')
    #zextests('zexall.com')
    return 0

def target(*args):
    return entry_point, None

if __name__ == "__main__":
    entry_point(())
